package org.jeecg.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class ProtocolCodec {

    // 解码UDP数据包
    public static ProtocolMessage decode(byte[] rawData) {
        ProtocolMessage msg = new ProtocolMessage();
        ByteBuffer buffer = ByteBuffer.wrap(rawData).order(ByteOrder.LITTLE_ENDIAN);

        // AP:C层解码
        msg.startFlag = buffer.get();
        msg.protocolType = buffer.get();
        msg.transportProtocolType = buffer.get();

        int dataLength = buffer.remaining() - 3; // 减去CRC(2)和结束标志(1)
        msg.data = new byte[dataLength];
        buffer.get(msg.data);
        msg.crc = buffer.getShort();
        msg.endFlag = buffer.get();

        // VP:A层解码
        ByteBuffer vpBuffer = ByteBuffer.wrap(msg.data).order(ByteOrder.LITTLE_ENDIAN);
        vpBuffer.get(msg.address);
        msg.packetId = vpBuffer.getShort();
        msg.vpFlag = vpBuffer.get();
        msg.cpid = vpBuffer.get();

        int mcpaLength = vpBuffer.remaining();
        msg.mcpaData = new byte[mcpaLength];
        vpBuffer.get(msg.mcpaData);

        // MCP:A层解码
        ByteBuffer mcpaBuffer = ByteBuffer.wrap(msg.mcpaData).order(ByteOrder.LITTLE_ENDIAN);
        msg.commandId = mcpaBuffer.get();
        msg.ackFlag = mcpaBuffer.get();

        while (mcpaBuffer.hasRemaining()) {
            MonitorData data = new MonitorData();
            data.length = mcpaBuffer.get();
            data.id = mcpaBuffer.getShort();

            int valueLength = data.length - 3; // 长度字段包含自身(1) + ID(2)
            if (valueLength > 0) {
                data.value = new byte[valueLength];
                mcpaBuffer.get(data.value);
            }
            msg.monitorDataList.add(data);
        }

        return msg;
    }

    // 编码协议消息为字节数组
    public static byte[] encode(ProtocolMessage msg) throws IOException {
        // 编码MCP:A层
        ByteArrayOutputStream mcpaStream = new ByteArrayOutputStream();
        mcpaStream.write(msg.commandId);
        mcpaStream.write(msg.ackFlag);

        for (MonitorData data : msg.monitorDataList) {
            mcpaStream.write(data.length);
            mcpaStream.write(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN)
                    .putShort(data.id).array());
            if (data.value != null) {
                mcpaStream.write(data.value, 0, data.value.length);
            }
        }
        msg.mcpaData = mcpaStream.toByteArray();

        // 编码VP:A层
        ByteArrayOutputStream vpStream = new ByteArrayOutputStream();
        vpStream.write(msg.address, 0, msg.address.length);
        vpStream.write(ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN)
                .putShort(msg.packetId).array());
        vpStream.write(msg.vpFlag);
        vpStream.write(msg.cpid);
        vpStream.write(msg.mcpaData, 0, msg.mcpaData.length);
        msg.data = vpStream.toByteArray();

        // 编码AP:C层
        ByteArrayOutputStream apStream = new ByteArrayOutputStream();
        apStream.write(msg.startFlag);
        apStream.write(msg.protocolType);
        apStream.write(msg.transportProtocolType);
        apStream.write(msg.data, 0, msg.data.length);

        // 计算CRC (简化实现)
        byte[] apData = apStream.toByteArray();
        short crc = calculateCRC(apData, 1, apData.length - 1); // 从protocolType开始

        ByteBuffer finalBuffer = ByteBuffer.allocate(apData.length + 3)
                .order(ByteOrder.LITTLE_ENDIAN)
                .put(apData)
                .putShort(crc)
                .put(msg.endFlag);

        return finalBuffer.array();
    }

    private static short calculateCRC(byte[] data, int offset, int length) {
        // 简化的CRC计算实现
        int crc = 0xFFFF;
        for (int i = offset; i < offset + length; i++) {
            crc = (crc ^ (data[i] & 0xFF)) & 0xFFFF;
            for (int j = 0; j < 8; j++) {
                if ((crc & 0x0001) == 1) {
                    crc = (crc >> 1) ^ 0x8408;
                } else {
                    crc = crc >> 1;
                }
            }
        }
        return (short) crc;
    }
}